home *** CD-ROM | disk | FTP | other *** search
/ Aminet 15 / Aminet 15 - Nov 1996.iso / Aminet / comm / bbs / s342q16.lha / vortex.c < prev    next >
C/C++ Source or Header  |  1996-08-29  |  18KB  |  573 lines

  1. /*
  2. *                               vortex.c
  3. *
  4. * Network "vortex" (aka infinite loop) handling code.
  5. */
  6. /*
  7. *                               history
  8. *
  9. * 88Oct23 HAW  Created.
  10. */
  11. #include "ctdl.h"
  12. #include "math.h"
  13. /*
  14. *                               contents
  15. *
  16. *       InitVortexing()         allocate & initialize buffers
  17. *       NotVortex()             checks for vortex possibility
  18. */
  19. /*
  20. * Globals
  21. */
  22. extern char logNetResults;
  23. extern char netDebug;
  24. /*
  25. * This should only be accessed in this code, so we shan't put it in
  26. * CTDL.H.  It's not "really" a global structure.
  27. */
  28. typedef struct
  29.   {
  30.   int       vRoomNo;    /* Room we are associated with */
  31.   AN_UNSIGNED vGen;       /* Allows checking for no longer in use */
  32.   MSG_NUMBER  vHighest;   /* Highest msg # received */
  33.   long      vLastDate;  /* Date of last message received */
  34.  
  35.   }
  36. VORTEX;
  37. /*
  38. * This structure serves the following purposes:
  39. * 1. Contain the data for one (1) vortex detection structure (VORTEX data);
  40. * 2. Remember what slot in the associated .vtx file this particular VORTEX
  41. *    data occupies (int Slot);
  42. * 3. Remember the # of the highest message encountered associated with this
  43. *    slot, for later updating when processing for this room is finished
  44. *    (MSG_NUMBER Current);
  45. * 4. Mark this particular instantiation of this structure type as actually in
  46. *    use (used iff Current != 0l);
  47. * 5. Mark this particular instantiation as having actually detected a vortex
  48. *    (char Detect).  This is for Aide> reporting.
  49. */
  50. typedef struct
  51.   {
  52.   int      Slot, SystemNum;
  53.   label    System;
  54.   MSG_NUMBER Current;
  55.   long       DateCurrent;
  56.   char       Detect;
  57.   VORTEX     data;
  58.  
  59.   }
  60. Vortex;
  61. void *CheckVt();
  62. static void FreeVt(Vortex *d);
  63. /*
  64. * VortexList
  65. *
  66. * This is a list of systems found during a session of adding messages.  The
  67. * list is used for performance reasons.  When a session is over this list
  68. * is used to update our records on disk all at once..
  69. */
  70. SListBase VortexList =
  71.   {
  72.   NULL, CheckVt, NULL, FreeVt, NULL
  73.  
  74.   };
  75. char VortexHandle = TRUE;
  76. /*
  77. * External variable definitions for NET.C
  78. */
  79. extern CONFIG    cfg;   /* Lots an lots of variables    */
  80. extern MessageBuffer   msgBuf;
  81. extern rTable    *roomTab;
  82. extern NetBuffer netTemp, netBuf;
  83. extern FILE      *netLog;
  84. extern int       thisRoom;
  85. extern aRoom     roomBuf;       /* Room buffer  */
  86. /*
  87. * -Vortex Handling-
  88. *
  89. *    Vortex: the phenomenon of messages showing up on other systems more than
  90. * once.  Usually caused by a "loop" of backbones.
  91. *
  92. *    Vortexes can be caused by a number of problems, and the direct cause
  93. * of any particular vortex should not be important.  However, there is
  94. * a method available, as designed by CrT, to detect and nullify vortexes.
  95. *
  96. *    The method is to track the message numbers coming in for each system
  97. * sending messages.  Each message is supposed to carry the number it was
  98. * assigned on the originating system in one of the transmitted field.
  99. * Therefore, by remembering the highest message received from a given system,
  100. * we may identify and scuttle all messages which we believe we've already
  101. * received.
  102. *
  103. *    We must be aware that some shared rooms will be backboned, thus causing
  104. * messages not directly from the calling node to be sent to here.  We should
  105. * check these messages, too.
  106. *
  107. *    The central question is what level of granularity should be used to track
  108. * each system?  There are clearly two options:
  109. *
  110. * 1) Keep a single MSG_NUMBER for each system on the nodelist.  This has
  111. *    the attraction of little complexity -- the variable may be kept in
  112. *    the netTab & CTDLNET.SYS files, and will only take up 4 bytes per
  113. *    node.  However, this ignores the problem of interrupted net sessions:
  114. *    a room that was successfully transferred and processed can easily contain
  115. *    higher number messages than a room which was interrupted and
  116. *    re-transferred, thus causing incorrect vortex detection.  Interrupted
  117. *    sessions, while certainly not the rule, are not uncommon, thus making
  118. *    this objection reasonable.  Additionally, implementation of this option
  119. *    would require a Major Release, which is not appealing at the moment.
  120. *
  121. * 2) Keep a MSG_NUMBER for each room that each system nets with.  This option
  122. *    is clearly far more complex, but it does solve the major problem of
  123. *    option 1.  Furthermore, it should not require a Major Release in order
  124. *    to implement, which makes it more attractive.  Unfortunately, it may
  125. *    require some rewriting of the getNet code in order to support multiple
  126. *    netBuf variables, but that should not be utterly ghastly (or so we
  127. *    pray).
  128. *
  129. *    Neither option handles virtual rooms, nor are they intended to.  At the
  130. * moment, such support is not seen as warranted.
  131. *
  132. *    The initial implementation of net vortexing will be of option 2).  If, at
  133. * a later date, it becomes clear that 2) is unworkable or unneeded for some
  134. * reason, we can switch to Option 1) easily at the next Major Release point.
  135. *
  136. * IMPLEMENTATION
  137. *
  138. *    In order to avoid a Major Release, we cannot change the structure of
  139. * CTDLNET.SYS.  Furthermore, it is not attractive to attempt to handle an
  140. * arbitrary number of shared rooms per node within CTDLNET.SYS -- if we attempt
  141. * to handle the problem in the same manner as shared rooms and net archive
  142. * rooms (not implemented as of yet, sigh), we will run into limits, perhaps
  143. * far too often.
  144. *
  145. *    Therefore, we shall handle vortex records in the following way.
  146. *
  147. * 1) Each node on a system's nodelist may or may not have a file named
  148. *    "#.vtx", where "#" is the index into CTDLNET.SYS for that record.  These
  149. *    files will be created as needed, and thus some or many nodes (in
  150. *    particular disabled nodes) will not have corresponding .vtx files.  All
  151. *    .vtx files will be located in the #NETAREA directory.
  152. *
  153. * 2) Any .vtx file will be composed of 1 or more records of type VORTEX.
  154. *    Refer to CTDL.H for details, but each record is made up of an integer
  155. *    associated with roomTab, a GenNumber so we can detect when this record
  156. *    is no longer really in use, and the highest Message # received.
  157. *
  158. * 3) C-86 will keep an internal list of VORTEX records, one per node, during
  159. *    net processing.  As a room is processed, each message will force a
  160. *    #.vtx access when the originating system is occuring for the first
  161. *    time during this processing.  If the .vtx file contains a record for
  162. *    this room, then it is loaded into the internal list and can be referenced
  163. *    for the balance of this room's processing without going to disk.  If
  164. *    no record is found or the #.vtx file does not exist, appropriate creation
  165. *    takes place.  If any message is found to be less than the last highest
  166. *    received for this room, it is discarded.  [An audit would probably be
  167. *    desirable, too.]
  168. *
  169. *    Please note that no #.vtx files will be created or utilized if the option
  170. * has not been enabled for this installation.  Currently, this option is
  171. * enabled by placing "+vortex" on the CTDL command line.  Only systems with
  172. * excess disk capacity should use this capability.
  173. */
  174. /*
  175. 88Oct19 from Thom Brown @ Utica College, NY
  176. Hue - about the new code that detects (and presumably rejects) vortexed
  177. messages.  Would it be possible to have Citadel post a message to the Aide
  178. I have this fear that now that we don't get vortexed messages there will be no
  179. attempt for offending systems to clean up their configurations, so to speak.
  180. I suppose a parallel message out to go back to the offending system as well.
  181. */
  182. /*
  183. * 90July
  184. *
  185. * The scheme mentioned above is now being rewritten to function independent
  186. * of CtdlNet.Sys.  A database will be constructed as new systems are
  187. * detected.
  188. */
  189. #define getVortex(x, y)         (fread(y, sizeof *(y), 1, x) == 1)
  190. #define putVortex(x, y)         fwrite(y, sizeof *(y), 1, x)
  191. typedef struct
  192.   {
  193.   label Id, Name;
  194.   char InUse;
  195.  
  196.   }
  197. VtRecord;
  198. static UNS_16 *VI;
  199. static UNS_16 TopVx = 0;
  200. extern char *READ_ANY, *APPEND_ANY;
  201. extern char *R_W_ANY, *WRITE_ANY;
  202. static int searchVortex(char *id, VtRecord *VtSystem);
  203. /*
  204. * VortexInit()
  205. *
  206. * This will initialize vortexing once and only once while an installation
  207. * is up.  It creates a dynamic table from the contents of vtxind.sys, an
  208. * index table.
  209. */
  210. void VortexInit()
  211.   {
  212.   FILE *fd;
  213.   SYS_FILE name;
  214.   long bytes;
  215.   if (!VortexHandle) return ;
  216.   makeSysName(name, "vtxind.sys", &cfg.netArea);
  217.   if (cfg.BoolFlags.debug)splitF(NULL, " VortexInit:%s\n",name);
  218.   if ((fd = fopen(name, READ_ANY)) == NULL) VI = NULL;
  219.   else
  220.     {
  221.     totalBytes(&bytes, fd);
  222.     VI = GetDynamic((int) bytes);
  223.     if (fread(VI, (int) bytes, 1, fd) != 1)
  224.     VI = NULL;
  225.     else
  226.     TopVx = (int) bytes/sizeof(*VI);
  227.     fclose(fd);
  228.  
  229.     }
  230.  
  231.   }
  232. /*
  233. * InitVortexing()
  234. *
  235. * This is an initialization function which should be called before each
  236. * session of checking messages in from a network session.  Although at
  237. * the moment it does nothing, it and the call to it should be retained
  238. * in the interests of completeness and convenience in case something needs
  239. * to be added at a later date.
  240. */
  241. void InitVortexing()
  242.   {
  243.  
  244.   }
  245. /*
  246. * NotVortex()
  247. *
  248. * This checks to see if the msg in msgBuf is vortexing or not.  FALSE is
  249. * returned if the message should be discarded.
  250. */
  251. char NotVortex()
  252.   {
  253.   int           NotUsed = -1, slot;
  254.   char  found, *low, IdAvailable = FALSE, DateAvailable = FALSE;
  255.   label       temp;
  256.   SYS_FILE    vortex;
  257.   FILE  *fd;
  258.   MSG_NUMBER  srcId = 0l;
  259.   long  srcDate = 0l;
  260.   extern char *READ_ANY;
  261.   Vortex      *Vtx;
  262.   VtRecord    VtSystem;
  263.   /* If no vortexing or we can't identify the nodeId ... */
  264.   if (!VortexHandle || !normId(msgBuf.mborig, temp))
  265.     {
  266.     if (cfg.BoolFlags.debug)splitF(NULL, " NotVortex:Not Vortexing or nodeid(%s) unknown\n",msgBuf.mborig);
  267.     return TRUE;
  268.     };
  269.   /*
  270.   * If system not in vortexlist, then we must add it first before continuing
  271.   * onwards.
  272.   */
  273.   if ((Vtx = SearchList(&VortexList, temp)) == NULL)
  274.     {
  275.     if ((slot = searchVortex(temp, &VtSystem)) == ERROR)
  276.       {
  277.       if( logNetResults)splitF(netLog, "Msg from unmonitored node %s @%s, adding it.\n",
  278.       msgBuf.mboname, msgBuf.mborig);
  279.       /* adding new element to list */
  280.       if (TopVx == 0)
  281.       VI = malloc(++TopVx * sizeof *VI);
  282.       else
  283.       VI = realloc(VI, ++TopVx * sizeof *VI);
  284.       VI[TopVx - 1] = hash(temp);
  285.       makeSysName(vortex, "vtxind.sys", &cfg.netArea);
  286.       if ((fd = fopen(vortex, WRITE_ANY)) == NULL)
  287.         {
  288.         printf("Problems with vortex index file.\n");
  289.         return FALSE;
  290.  
  291.         }
  292.       fwrite(VI, 1, TopVx * sizeof *VI, fd);
  293.       fclose(fd);
  294.       strCpy(VtSystem.Id, temp);
  295.       strCpy(VtSystem.Name, msgBuf.mboname);
  296.       VtSystem.InUse = TRUE;
  297.       makeSysName(vortex, "vortex.sys", &cfg.netArea);
  298.       if ((fd = fopen(vortex, (TopVx == 1) ? WRITE_ANY : APPEND_ANY))
  299.       == NULL)
  300.         {
  301.         printf("Problems with vortex file.\n");
  302.         return FALSE;
  303.  
  304.         }
  305.       fwrite(&VtSystem, 1, sizeof VtSystem, fd);
  306.       fclose(fd);
  307.       slot = TopVx - 1;
  308.  
  309.       }
  310.     else   if (cfg.BoolFlags.debug)splitF(NULL, "searchVortex:found\n");
  311.  
  312.     }
  313.   else slot = Vtx->SystemNum;
  314.   if (Vtx == NULL)
  315.     {
  316.     /*
  317.     * Check to see if this file exists
  318.     */
  319.     Vtx = GetDynamic(sizeof *Vtx);
  320.     AddData(&VortexList, Vtx, NULL, FALSE);
  321.     strCpy(Vtx->System, temp);
  322.     Vtx->SystemNum = slot;
  323.     sPrintf(temp, "%d.vex", slot);
  324.     makeSysName(vortex, temp, &cfg.netArea);
  325.     if (cfg.BoolFlags.debug)splitF(NULL, "Open %s\n",vortex);
  326.     if ((fd = safeopen(vortex, READ_ANY)) != NULL)
  327.       {
  328.       /*
  329.       * File is here, so search for a record corresponding to this room.
  330.       */
  331.       Vtx->Slot = 0;
  332.       while ((found = getVortex(fd, &Vtx->data)))
  333.         {
  334.         /* First, keep an eye out for recycling */
  335.         if (Vtx->data.vGen !=
  336.         roomTab[Vtx->data.vRoomNo].rtgen)
  337.         NotUsed = Vtx->Slot;      /* mark it */
  338.         else if (Vtx->data.vRoomNo == thisRoom)
  339.         break;      /* Found it, so stop here */
  340.         Vtx->Slot++;
  341.  
  342.         }
  343.       if (!found)
  344.         {
  345.         /* Garbage collection measure */
  346.         if (NotUsed != -1)
  347.         Vtx->Slot = NotUsed;
  348.  
  349.         }
  350.       fclose(fd);
  351.  
  352.       }
  353.     if (fd == NULL)
  354.     Vtx->Slot = 0;
  355.     if (fd == NULL || !found)
  356.       {
  357.       Vtx->data.vRoomNo = thisRoom;
  358.       Vtx->data.vGen = roomTab[thisRoom].rtgen;
  359.       /* this shouldn't hurt */
  360.       Vtx->data.vHighest = 1l;
  361.  
  362.       }
  363.  
  364.     }
  365.   /*-Construct srcId number here-*/
  366.   if (strLen(msgBuf.mbsrcId) != 0)
  367.     {
  368.     IdAvailable = TRUE;
  369.     if ((low = strchr(msgBuf.mbsrcId, ' ')) == NULL)
  370.     srcId = atol(msgBuf.mbsrcId);
  371.     else
  372.     srcId = (atol(msgBuf.mbsrcId) << 16) + atol(low + 1);
  373.  
  374.     }
  375.   if (strLen(msgBuf.mbdate) != 0 || strLen(msgBuf.mbtime) != 0)
  376.   if ((DateAvailable = ReadDate(msgBuf.mbdate, &srcDate)))
  377.   srcDate += ReadTime(msgBuf.mbtime);
  378.   /*
  379.   * Comparison note:
  380.   * The current scheme involves both the date of the message and the
  381.   * source ID of the message.  This gives us security against both
  382.   * incorrect system dates and message base resetting.  Unfortunately,
  383.   * STadel does not transmit source IDs.  Therefore, the rules for
  384.   * comparison are a little obscure.  Basically, the following if
  385.   * boils down to this:
  386.   *  o if the source id is missing, compare against the latest date
  387.   *    of a message received in this room for the source system of
  388.   *    this message.  The comparison is NON-inclusive (i.e., strictly
  389.   *    less than), because it is conceivable for two net messages to
  390.   *    contain the same date and time -- message composition is not
  391.   *    always a lengthy process.  Note this leaves a "hole" in the
  392.   *    vortex detection department -- we don't dare test non-strictly,
  393.   *    since that can result in losing net messages, but this lets
  394.   *    certain vortexed messages get through.
  395.   *  o if both date and source id are available, then the message
  396.   *    must fail both tests before it is rejected.  That's our
  397.   *    fail-safe mentioned earlier.  During this* comparison, we
  398.   *    use an inclusive (non-strict) comparison of message dates,
  399.   *    because the message # check will take care of distinguishing
  400.   *    between two messages written under the same date/time stamp.
  401.   *    Therefore, there's no hole for us to worry about.  That
  402.   *    difference is what makes this if so scary looking.
  403.   */
  404.   if ((!IdAvailable || srcId <= Vtx->data.vHighest) &&
  405.   (!DateAvailable || (IdAvailable && srcDate <= Vtx->data.vLastDate) ||
  406.   (!IdAvailable && srcDate < Vtx->data.vLastDate)))
  407.     {
  408.     if( logNetResults && netDebug)splitF(netLog, "%s from %s rejected.\n", msgBuf.mbsrcId,
  409.     msgBuf.mboname);
  410.     Vtx->Detect = TRUE;
  411.     return FALSE;
  412.  
  413.     }
  414.   else
  415.     {
  416.     if (IdAvailable)
  417.     Vtx->Current = max(Vtx->Current, srcId);
  418.     if (DateAvailable)
  419.     Vtx->DateCurrent = max(Vtx->DateCurrent, srcDate);
  420.     return TRUE;
  421.  
  422.     }
  423.  
  424.   }
  425. static int errors;
  426. /*
  427. * FinVortexing()
  428. *
  429. * This function should be called to finish a vortex checking session.  This
  430. * saves updates to disk.
  431. */
  432. void FinVortexing()
  433.   {
  434.   if (!VortexHandle)
  435.   return;
  436.   sPrintf(msgBuf.mbtext, "Vortex attempt by %s in %s, involving system(s) ",
  437.   netBuf.netName, roomBuf.rbname);
  438.   errors = 0;
  439.   KillList(&VortexList);
  440.   if (errors)
  441.     {
  442.     strCat(msgBuf.mbtext, ".");
  443.     netResult(msgBuf.mbtext);
  444.  
  445.     }
  446.  
  447.   }
  448. /*
  449. * FreeVt()
  450. *
  451. * This will write and free an element of the vortex list; obviously, it is
  452. * used in list handling.
  453. */
  454. static void FreeVt(Vortex *d)
  455.   {
  456.   label    temp;
  457.   SYS_FILE vortex;
  458.   FILE     *fd;
  459.   VtRecord VtSystem;
  460.   if (d->Current > d->data.vHighest)
  461.   d->data.vHighest = d->Current;
  462.   if (d->DateCurrent > d->data.vLastDate)
  463.   d->data.vLastDate = d->DateCurrent;
  464.   sPrintf(temp, "%d.vex", d->SystemNum);
  465.   makeSysName(vortex, temp, &cfg.netArea);
  466.   if ((fd = safeopen(vortex, R_W_ANY)) == NULL)
  467.     {
  468.     if ((fd = safeopen(vortex, WRITE_ANY)) == NULL)
  469.       {
  470.       if( logNetResults && netDebug) splitF(netLog, "Couldn't create %s!!\n", vortex);
  471.  
  472.       }
  473.  
  474.     }
  475.   else
  476.     {
  477.     fseek(fd, d->Slot * sizeof d->data, 0);
  478.  
  479.     }
  480.   if (fd != NULL)
  481.     {
  482.     putVortex(fd, &d->data);
  483.     fclose(fd);
  484.  
  485.     }
  486.   if (d->Detect)
  487.     {
  488.     errors++;
  489.     if (searchVortex(d->System, &VtSystem) != ERROR)
  490.     sPrintf(lbyte(msgBuf.mbtext), (errors == 1) ? "%s" : ", %s",
  491.     VtSystem.Name);
  492.  
  493.     }
  494.   free(d);
  495.  
  496.   }
  497. /*
  498. * CheckVt()
  499. *
  500. * This function is used to search the list of vortex records for a given
  501. * system.
  502. */
  503. void *CheckVt(Vortex *d, char *s)
  504.   {
  505.   return (strCmpU(d->System, s) == SAMESTRING) ? d : NULL;
  506.  
  507.   }
  508. /*
  509. * ReadTime()
  510. *
  511. * This function reads the time stamp of a message and returns a long
  512. * integer indicating the absolute time of the message in seconds past
  513. * midnight.  It does NOT handle time zones.
  514. */
  515. long ReadTime(char *time)
  516.   {
  517.   char *s, afternoon;
  518.   long ret;
  519.   if (strLen(time) == 0) return 0l;
  520.   if ((s = strrchr(time, ' ')) == NULL) return 0l;
  521.   if (strCmpU(s + 1, "am") == SAMESTRING) afternoon = FALSE;
  522.   else if (strCmpU(s + 1, "pm") == SAMESTRING) afternoon = TRUE;
  523.   else return 0l;
  524.   if ((s = strchr(time, ':')) == NULL) return 0l;
  525.   ret = atol(time) * 60l;
  526.   if (afternoon)
  527.     {
  528.     if (atol(time) != 12) ret += 720l;
  529.  
  530.     }
  531.   else if (atol(time) == 12) ret = 0l;
  532.   ret += atol(s + 1);
  533.   return 60 * ret;
  534.  
  535.   }
  536. /*
  537. * searchVortex()
  538. *
  539. * This function will search the vortex list for the given system.  It
  540. * return ERROR if the system is not in the database, otherwise an index
  541. * into it in terms of records.
  542. */
  543. static int searchVortex(char *id, VtRecord *VtSystem)
  544.   {
  545.   int      rover;
  546.   UNS_16   hval;
  547.   FILE     *fd;
  548.   SYS_FILE name;
  549.   makeSysName(name, "vortex.sys", &cfg.netArea);
  550.   if ((fd = fopen(name, READ_ANY)) == NULL) return ERROR;
  551.   hval = hash(id);
  552.   for (rover = 0; rover < TopVx; rover++)
  553.     {
  554.     if (VI[rover] == hval)
  555.       {
  556.       if (fd == NULL)
  557.       if ((fd = fopen(name, READ_ANY)) == NULL) return ERROR;
  558.       fseek(fd, rover * sizeof *VtSystem, 0);
  559.       if (fread(VtSystem, sizeof *VtSystem, 1, fd) > 0)
  560.         {
  561.         if (strCmpU(id, VtSystem->Id) == SAMESTRING) break;
  562.  
  563.         }
  564.  
  565.       }
  566.  
  567.     }
  568.   if (fd != NULL)
  569.   fclose(fd);
  570.   return (rover == TopVx) ? ERROR : rover;
  571.  
  572.   }
  573.